IAMユーザのシークレットアクセスキーをSecret Managerに保管しローテーションさせてみた
はじめに
プログラム実行に利用するIAMユーザのアクセスキーとシークレットアクセスキーを、半年に1度手動でローテーションしていました。手作業だと忘れがちということに加えて、セキュリティリスクもあるので、自動化しようと思いました。今回は、その具体的な構成とスクリプトについて紹介いたします。
前提条件
次を前提条件としています。
- SAM(Serverless Application Model)がわかる
- samコマンドを利用できる
構成について
概念図
- Secrets Managerのローテーション機能を利用し、Lambda関数を実行します。
- Lambda関数でIAMユーザのシークレットアクセスキーをローテーションさせます。
- 新しいシークレットアクセスキーをSecrets Managerに保存します。
全体の構成はこのようになっています。
スケジュール起動=EventBridgeとなりがちですが、
Secrets Managerから直接Lambda関数を呼び出す機能で実装するとシンプルな構成になります。
必要な権限
Secrets ManagerがLambda関数を呼び出す権限
Secrets ManagerがLambda関数を呼び出す場合、リソースベースポリシーステートメントでSecrets Managerに次の権限を与える必要があります。
lambda:InvokeFunction
Lambda関数がIAMユーザのアクセスキーを編集できる権限
Lambda関数がIAMユーザのアクセスキーを編集するためには、次の権限が必要です。
iam:CreateAccessKey
iam:DeleteAccessKey
iam:UpdateAccessKey
iam:ListAccessKeys
Lambda関数がシークレットアクセスキーをSecrets Managerに保存する権限
Lambda関数がシークレットアクセスキーをSecrets Managerに保存するためには、次の権限が必要です。
secretsmanager:DescribeSecret
secretsmanager:GetSecretValue
secretsmanager:PutSecretValue
secretsmanager:UpdateSecretVersionStage
secretsmanager:ListSecretVersionIds
Lambda関数がCloudWatchにログを出力する権限
Lambda関数がCloudWatchにログを出力するには、次のマネージドポリシーが必要です。
AWSLambdaBasicExecutionRole
デプロイ
サンプルデータの注意点
説明のため、コードを簡単にする必要があります。実際にご利用の際は、適宜修正を加えてください。
- 簡単のため、例外処理は入れていません。
- IAMユーザの権限はサンプル用にバケットの一覧を取得できるようにしています。
- IAMユーザ名はパラメータで取得するようにしています。
ファイル構成
構成は以下になるようにしてください。
IAMAccessKeyRotation
├── requirements.txt
├── template.yaml
└── src
└── iamuser-secret-access-key-rotation-handler.py
Lambda関数
Lambda関数の役割は以下になっています。
簡単のため、例外処理は記載していませんが、必要であれば追記してください。
import boto3
import json
import os
secretsmanager_client = boto3.client('secretsmanager')
iam_client = boto3.client('iam')
def handler(event, context):
# 環境変数からシークレット名を取得
username = os.environ["IAM_USER_NAME"]
# イベントからデータを取得
RotationToken = event['RotationToken']
secret_arn = event['SecretId']
# IAMユーザのアクセスキーをローテーションする
updated_secret = rotate_iamuser_access_key(username)
# Secrets Managerに新しいアクセスキーを保存
save_secret_access_key_in_secret_manager(secret_arn,updated_secret,RotationToken)
def rotate_iamuser_access_key(username:str):
"""
アクセスキーが2個ある場合、1つ削除後、新しく1つ作成する
"""
# 2個ある場合、古い方のアクセスキーを削除
# アクセスキーの一覧を取得
existing_keys = iam_client.list_access_keys(UserName=username)['AccessKeyMetadata']
# 2個あったら最も古いアクセスキーを削除
if len(existing_keys) == 2:
# min()を使い最古値を取得
older_key = min(existing_keys, key=lambda x: x['CreateDate'])
iam_client.delete_access_key(
UserName=username,
AccessKeyId=older_key['AccessKeyId']
)
# IAMユーザで新しいアクセスキーを作成
response = iam_client.create_access_key(UserName=username)
new_access_key_id = response['AccessKey']['AccessKeyId']
new_secret_access_key = response['AccessKey']['SecretAccessKey']
print(f'ACCESS KEYを作成しました。')
# シークレットを更新
updated_secret = {
"accessKey": new_access_key_id,
"secretKey": new_secret_access_key
}
return updated_secret
def save_secret_access_key_in_secret_manager(secret_arn:str,updated_secret:dict,RotationToken:str):
"""
Secrets Managerに新しいアクセスキーを保存
"""
secretsmanager_client.put_secret_value(
SecretId=secret_arn,
SecretString=json.dumps(updated_secret),
RotationToken=RotationToken
)
モジュールについて
必要なモジュールはrequirements.txt
にまとめています。
boto3
SAMの構成について
以下のyamlファイルに構成をまとめています。
権限と構成については上に示した通りになっています。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for creating an IAM user and setting up Secrets Manager
Parameters:
IAMUserName:
Type: String
Description: "The name of the IAM user whose access keys will be rotated."
Resources:
IAMUser:
Type: AWS::IAM::User
Properties:
UserName: !Ref IAMUserName
Policies:
- PolicyName: S3ListBucketsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:ListAllMyBuckets
Resource: "*"
IAMUserSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "${IAMUserName}-secret"
Description: "Secret for IAM user access key"
SecretString: "{}"
iamuserSecretAccessKeyRotationHandlerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SecretsManagerPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:DescribeSecret
- secretsmanager:GetSecretValue
- secretsmanager:PutSecretValue
- secretsmanager:UpdateSecretVersionStage
- secretsmanager:ListSecretVersionIds
Resource: !Ref IAMUserSecret
- PolicyName: IAMAccessKeyPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- iam:CreateAccessKey
- iam:DeleteAccessKey
- iam:UpdateAccessKey
- iam:ListAccessKeys
Resource: !GetAtt IAMUser.Arn
iamuserSecretAccessKeyRotationHandler:
Type: AWS::Serverless::Function
Properties:
FunctionName: iamuser-secret-access-key-rotation-handler
Handler: iamuser-secret-access-key-rotation-handler.handler
Runtime: python3.9
CodeUri: ./src
Role: !GetAtt iamuserSecretAccessKeyRotationHandlerRole.Arn
Environment:
Variables:
IAM_USER_NAME: !Ref IAMUserName
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref iamuserSecretAccessKeyRotationHandler
Action: lambda:InvokeFunction
Principal: secretsmanager.amazonaws.com
SourceArn: !Ref IAMUserSecret
SecretRotationSchedule:
Type: AWS::SecretsManager::RotationSchedule
Properties:
SecretId: !Ref IAMUserSecret
RotationLambdaARN: !GetAtt iamuserSecretAccessKeyRotationHandler.Arn
RotationRules:
AutomaticallyAfterDays: 30
Outputs:
RotationFunctionArn:
Description: "ARN of the IAM Access Key Rotation Lambda function"
Value: !GetAtt iamuserSecretAccessKeyRotationHandler.Arn
デプロイ
IAMAccessKeyRotationに移動後、以下のスクリプトを順番に実行していきます。
スクリプトを修正するたび、ビルドスクリプトから実行し直す必要があります。
sam build
sam deploy \
--guided
--capabilities CAPABILITY_NAMED_IAM \
--profile <YOUR-PROFILE>
-
--guided
: デプロイプロセスが対話型モードになり、初めてデプロイする際や設定を確認したいときに便利です。 -
--profile
<YOUR-PROFILE> : AWS CLIプロファイルを指定します。 -
--capabilities CAPABILITY_NAMED_IAM
: CloudFormationがIAMリソースを作成・変更するための権限を許可するためのオプションです。
実行確認
セキュリティ上の観点から、シークレットアクセスキーがローテーションしているところはお見せできませんが、アクセスキーがローテーションしていれば、シークレットアクセスキーもローテーションしています。
IAMユーザのシークレットキーが入れ替わっているか
ローテーション前のアクセスキー
ローテーション後のアクセスキー
結果
ローテーションが行われたことにより***********LQEE
が削除され、***********IM7F
が追加されています。
IAMユーザのシークレットアクセスキーが新しくなっているか
ローテーション前
ローテーション後
結果
***********SEEM
から***********IM7F
に変わった
バージョンが入れ替わっているか
ローテーション前のバージョン
ローテーション後のバージョン
結果
ローテーションされた結果、ローテーション前のAWSCURRENT
がAWSPREVIOUS
にかわっています。